package core.java;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Arrays;
import java.util.concurrent.*;

public class AioServer {
    final private int PROT;
    final private String IP;

    public AioServer(int PROT) {
        this(PROT,"127.0.0.1");
    }

    public AioServer(int PROT, String IP) {
        this.PROT = PROT;
        this.IP = IP;
    }
    public void start() throws IOException {
        ExecutorService exec = new ThreadPoolExecutor(0,10,60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
        AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withThreadPool(exec);
        AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open(channelGroup);
        serverSocketChannel.bind(new InetSocketAddress(IP,PROT));
        serverSocketChannel.accept(null,new AccpetTimerHander(serverSocketChannel));
        Object o = new Object();
        synchronized(o){
            try {
                o.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    class AccpetTimerHander implements CompletionHandler<AsynchronousSocketChannel, Integer> {
        private AsynchronousServerSocketChannel serverSocketChannel;

        public AccpetTimerHander(AsynchronousServerSocketChannel serverSocketChannel) {
            this.serverSocketChannel = serverSocketChannel;
        }


        @Override
        public void completed(AsynchronousSocketChannel socketChannel, Integer integer) {
             this.serverSocketChannel.accept(null,this);
             ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
             socketChannel.read(byteBuffer,null,new AccpetReadHander(byteBuffer,socketChannel));
        }

        @Override
        public void failed(Throwable throwable, Integer integer) {
            System.err.println(throwable.getMessage());
        }
    }
    class AccpetReadHander implements CompletionHandler<Integer , Integer> {
        private ByteBuffer byteBuffer;
        private AsynchronousSocketChannel socketChannel;

        public AccpetReadHander(ByteBuffer byteBuffer, AsynchronousSocketChannel socketChannel) {
            this.byteBuffer = byteBuffer;
            this.socketChannel = socketChannel;
        }

        @Override
        public void completed(Integer integer, Integer integer2) {
            String s = null;
            try {
                s = new String(Arrays.copyOfRange(byteBuffer.array(), 0, byteBuffer.position()),"utf-8");
                if (integer == -1 || s.indexOf("exit") != -1) {
                    socketChannel.close();
                    return;
                }
                System.out.println(s+" "+byteBuffer);
                int endIndex = s.indexOf("endl");
                if (endIndex != -1) {
                    s = s.substring(0, endIndex);
                    byteBuffer.limit(s.getBytes("utf-8").length);
                }
                byteBuffer.flip();
                Future<Integer> future =  socketChannel.write(byteBuffer);
                future.get();
                if (endIndex!=-1){
                    byteBuffer.clear();
                    byteBuffer.put("endl".getBytes("utf-8"));
                    byteBuffer.flip();
                    future = socketChannel.write(byteBuffer);
                    future.get();
                    byteBuffer.clear();
                }else {
                    byteBuffer.compact();
                }
                socketChannel.read(byteBuffer,null,this);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void failed(Throwable throwable, Integer integer) {
            System.err.println(throwable.getMessage());
        }
    }
    public static void main ( String[] args) throws IOException {
        new AioServer(8000).start();

    }
}
